package com.globallogic.javatrainings.sergiiivanov.model;

import java.io.Serializable;

import com.globallogic.javatrainings.sergiiivanov.resources.ErrorCodes;

/**
 * Abstract class that implements storage for people.<br>
 * Includes collection of {@link ResumeEntry}<br>
 * 
 * @author Sergey
 * 
 */
abstract public class Person implements Serializable, MyArrayToNamesCovertable {
    private static final long serialVersionUID = 1L;
    private String email;
    private String mobile;
    private String name;
    private MyArray<ResumeEntry> resume;
    private MyArray<NameUniqueObserver> observers;

    /**
     * Default constructor
     */
    public Person() {
        observers = new MyArray<NameUniqueObserver>(new NameUniqueObserver[0]);
        resume = new MyArray<ResumeEntry>(new ResumeEntry[0]);
    }

    /**
     * Quick constructor
     * 
     * @param newName
     *            - name of personnel. not null, not empty
     * @param newMobile
     *            - mobile phone (free text)
     * @param newEmail
     *            - email (free text)
     * @throws UniversityWarning
     *             when name is null or empty (Const.STRING_EMPTY)
     */
    public Person(String newName, String newMobile, String newEmail) throws UniversityWarning {
        if (newName == null || Const.STRING_EMPTY.equals(newName)) {
            throw new UniversityWarning(ErrorCodes.PERSON_NAME_NULL);
        }
        this.name = newName;
        this.mobile = newMobile == null ? Const.STRING_EMPTY : newMobile;
        this.email = newEmail == null ? Const.STRING_EMPTY : newEmail;
        resume = new MyArray<ResumeEntry>(new ResumeEntry[0]);
        observers = new MyArray<NameUniqueObserver>(new NameUniqueObserver[0]);
    }

    /**
     * Adds name uniqueness validation observer
     * 
     * @param observer
     *            - implementation of {@link NameUniqueObserver}
     */
    protected void addObserver(NameUniqueObserver observer) {
        if (observers.indexOf(observer) < 0) {
            observers.add(observer);
        }
    }

    /**
     * Adds resume entry that will be used in {@link #getResumeEntry(int)} and
     * {@link #getResumePrint()}
     * 
     * @param year
     *            - free text
     * @param summary
     *            - free text
     * @param notes
     *            - free text
     */
    public void addResumeEntry(String year, String summary, String notes) {
        this.resume.add(new ResumeEntry(year, summary, notes));
    }

    /**
     * Adds ResumeEntry parsed from array String[3]{year, summary, notes}
     * 
     * @param resumeEntryDetails
     *            - String[3]{year, summary, notes}
     * @throws UniversityWarning
     *             with message ErrorCodes.RESUME_WRONG_DATA_FORMAT if provided
     *             array is wrong
     * @see #addResumeEntry(String, String, String)
     */
    public void addResumeEntry(String[] resumeEntryDetails) throws UniversityWarning {
        if (resumeEntryDetails == null || resumeEntryDetails.length < 3) {
            throw new UniversityWarning(ErrorCodes.RESUME_WRONG_DATA_FORMAT);
        }
        addResumeEntry(
                resumeEntryDetails[0],
                resumeEntryDetails[1],
                resumeEntryDetails[2]);
    }

    private void checkResumeIndex(int index) throws UniversityWarning {
        if (index < 0 || countResumeEntries() <= index) {
            throw new UniversityWarning(ErrorCodes.RESUME_ENTRY_NOT_FOUND_BY_INDEX + Integer.toString(index));
        }
    }

    /**
     * Calculates number of resume entries
     * 
     * @return number of entries
     */
    public int countResumeEntries() {
        return resume.size();
    }

    /**
     * @return email as it was set by {@link #setEmail(String)}
     */
    public String getEmail() {
        return email;
    }

    /**
     * @return mobile as it was set by {@link #setMobile(String)}
     */
    public String getMobile() {
        return mobile;
    }

    /**
     * @return name as it was set by {@link #setName(String)}
     */
    @Override
    public String getName() {
        return name;
    }

    /**
     * Returns {@link ResumeEntry} stored under index
     * 
     * @param index
     *            of entry, 0 <= index < {@link #countResumeEntries()}
     * @return {@link ResumeEntry}
     * @throws UniversityWarning
     *             when index is wrong
     * @throws UniversityFailure
     *             when code crashes
     */
    public ResumeEntry getResumeEntry(int index) throws UniversityFailure, UniversityWarning {
        checkResumeIndex(index);
        return resume.get(index);
    }

    /**
     * Returns print-ready table String[][] of resume records<br>
     * Format of each line is the same as in {@link #addResumeEntry(String[])}
     * 
     * @return String[][] (empty array if no entries stored)
     */
    public String[][] getResumePrint() {
        MyArray<String[]> res = new MyArray<String[]>(new String[0][3]);
        for (ResumeEntry row : resume) {
            res.add(row.asArray());
        }
        return res.toArray();
    }

    /**
     * Asks all linked observers, added by
     * {@link #addObserver(NameUniqueObserver)}<br>
     * about duplicated names
     * 
     * @param newName
     *            - name to find<br>
     *            Do not use with name from the same collection - it will find
     *            itself
     * @throws UniversityWarning
     *             when name found in observer's collection
     */
    protected void hasDuplicate(String newName) throws UniversityWarning {
        for (NameUniqueObserver c : observers) {
            if (c != null)
                c.hasDuplicates(newName);
        }
    }

    /**
     * REmoves resume entry from the collection
     * 
     * @param index
     *            - valid index of entry, 0<=index<{@link #countResumeEntries()}
     * @throws UniversityWarning
     *             index is wrong
     * @throws UniversityFailure
     *             code crashes
     */
    public void removeResumeEntry(int index) throws UniversityWarning, UniversityFailure {
        checkResumeIndex(index);
        resume.remove(index);
    }

    /**
     * Updates email as provided
     * 
     * @param newEmail
     *            String to set (null applicable)
     */
    public void setEmail(String newEmail) {
        this.email = newEmail;
    }

    /**
     * Updates mobile as provided
     * 
     * @param newMobile
     *            String to set (null applicable)
     */
    public void setMobile(String newMobile) {
        this.mobile = newMobile;
    }

    /**
     * Updates name if new name is valid: not null, not empty, unique within the
     * scope of observers added by {@link #addObserver(NameUniqueObserver)}
     * 
     * @param newName
     *            valid name
     * @throws UniversityWarning
     *             name is not valid
     */
    public void setName(String newName) throws UniversityWarning {
        if (newName == null || Const.STRING_EMPTY.equals(newName))
            throw new UniversityWarning(ErrorCodes.PERSON_NAME_NULL);
        hasDuplicate(newName);
        this.name = newName;
    }

    protected void resumeClear() {
        resume.clear();
    }
}
